#include <SFML/Graphics/Texture.hpp>
#include <SFML/Graphics/Sprite.hpp>
#include <SFML/Graphics/RenderTarget.hpp>
#include <nice2d/application.h>
#include <nice2d/sprite.h>
#include <nice2d/spritedecorator.h>
#include <nice2d/bitmask.h>
#include <nice2d/spritecontroller.h>
#include <nice2d/components/imagemanager.h>
#include <nice2d/components/group.h>
#include <nice2d/components/spritestatus.h>
#include <nice2d/components/controllable.h>

IMPLEMENT_OBJECT(Sprite, Entity)

class SpriteData
{
public:
    bool inited = false;
    std::vector<sf::Sprite> sprites;
    sf::Color spriteColor = sf::Color::White;
    SpriteDecoratorPointer spriteDecorator;
    std::weak_ptr<Sprite> spriteOwner;
    std::list<std::pair<std::string, sf::IntRect>> images;
    std::shared_ptr<SpriteController> spriteController;
    bool dirtySpriteColor = true;
    bool done = false;
};

SpritePointer Sprite::create(const std::string &image, const sf::Vector2f &center)
{
    auto sprite = std::make_shared<Sprite>();
    auto textureManager = GET_COMPONENT(Application::getInstance(), ImageManager);
    auto texture = textureManager->loadTextureFromFile(image);
    sprite->setSize(texture->getSize().x, texture->getSize().y);
    sprite->addSpriteImage(image);
    sprite->setOrigin(texture->getSize().x * 0.5f, texture->getSize().y * 0.5f);
    sprite->setCenter(center.x, center.y);
    return sprite;
}

Sprite::Sprite():
    data(new SpriteData())
{
    ADD_COMPONENT(this, Group);
    ADD_COMPONENT(this, Controllable);
    ADD_COMPONENT(this, SpriteStatus);
}

Sprite::~Sprite()
{
}

void Sprite::setSpriteOwner(SpritePointer owner)
{
    if (owner != data->spriteOwner.lock()) {
        data->spriteOwner = owner;
        onOwnerChanged();
    }
}

SpritePointer Sprite::getSpriteOwner() const
{
    return data->spriteOwner.lock();
}

void Sprite::setDone(bool done)
{
    data->done = done;
}

bool Sprite::isDone() const
{
    return data->done;
}

void Sprite::setSpriteColor(const sf::Color &color)
{
    data->spriteColor = color;
    data->dirtySpriteColor = true;
}

sf::Color Sprite::getSpriteColor() const
{
    return data->spriteColor;
}

void Sprite::setSpriteController(std::shared_ptr<SpriteController> controller)
{
    data->spriteController = controller;
}

std::shared_ptr<SpriteController> Sprite::getSpriteController() const
{
    return data->spriteController;
}

void Sprite::addSpriteImage(const std::string &file, const sf::IntRect &area)
{
    auto textureManager = GET_COMPONENT(Application::getInstance(), ImageManager);
    auto texture = textureManager->loadTextureFromFile(file);
    assert(texture);

    sf::Sprite sprite;
    sprite.setColor(data->spriteColor);
    sprite.setTexture(*texture);
    if (area.width != 0)
        sprite.setTextureRect(area);
    data->sprites.push_back(sprite);
}

void Sprite::setSpriteImageSet(std::shared_ptr<ImageSet> imageset)
{

}

void Sprite::setDecorator(SpriteDecoratorPointer spriteDecorator)
{
    data->spriteDecorator = spriteDecorator;
}

void Sprite::clearDecorator()
{
    data->spriteDecorator.reset();
}

void Sprite::onUpdateEntity(float deltaTime)
{
    if (data->dirtySpriteColor) {
        data->dirtySpriteColor = false;

        for (auto itr = data->sprites.begin(); itr != data->sprites.end(); itr++) {
            (*itr).setColor(data->spriteColor);
        }
    }

    if (!data->inited) {
        data->inited = true;
        visitComponent([&](std::pair<std::string, ComponentPointer> pair) {
            pair.second->setOwner(as<Node>());
        });
    }

    if (data->spriteController)
        data->spriteController->updateSprite(as<Sprite>(), deltaTime);
    onUpdateSprite(deltaTime);
}

void Sprite::onUpdateSprite(float deltaTime)
{
}

void Sprite::onDrawEntity(sf::RenderTarget &target, sf::RenderStates states) const
{
    if (data->sprites.empty())
        Entity::onDrawNode(target, states);

    if (data->spriteDecorator)
        target.draw(*data->spriteDecorator, states);

    for (auto itr = data->sprites.begin(); itr != data->sprites.end(); itr++) {
        target.draw(*itr, states);
    }

    onDrawSprite(target, states);
}

void Sprite::onDrawSprite(sf::RenderTarget &target, sf::RenderStates states) const
{
}


/*
PTree Sprite::serialize()
{
    PTree node;
    SERIALIZE_BASE_OBJECT(node, Entity)
    node.add("controllable", isControllable());
    node.add_child("color", SerializerHelper::serialize(getSpriteColor()));
    node.add("done", isDone());

    ptree images;
    for (const auto &image : data->images) {
        ptree item;
        item.add("file", image.first);
        item.add_child("area", SerializerHelper::serialize(image.second));
        images.add_child("item", item);
    }
    node.add_child("images", images);
    return node;
}

void Sprite::deserialize(const PTree &node)
{
    DESERIALIZE_BASE_OBJECT(node, Entity)

    {
        auto option = node.get_optional<bool>("controllable");
        if (option.has_value())
            setControllable(option.value());
    }

    setSpriteColor(DeserializerFactory::deserializeColor(node.get_child("color")));
    GET_DESERIALIZE_VALUE_IN_DATA(node, done)

    auto option = node.get_child_optional("images");
    if (option.has_value()) {
        auto images = option.value();
        auto itr = images.begin();
        while (itr != images.end()) {
            if (itr->first == "item") {
                auto file = itr->second.get<std::string>("file");
                auto area = DeserializerFactory::deserializeIntRect(itr->second.get_child("area"));
                addTexture(file, area);
            }
            itr ++;
        }
    }
}
*/

void Sprite::onSpriteStatusChanged()
{
    if (data->spriteDecorator)
        data->spriteDecorator->update();
}

void Sprite::onControllableChanged()
{
    if (data->spriteDecorator)
        data->spriteDecorator->update();
}

void Sprite::onPositionChanged()
{
    if (data->spriteDecorator)
        data->spriteDecorator->update();
}

void Sprite::onSizeChanged()
{
    if (data->spriteDecorator)
        data->spriteDecorator->update();
}

void Sprite::onRotateChanged()
{
    if (data->spriteDecorator)
        data->spriteDecorator->update();
}

void Sprite::onScaleChanged()
{
    if (data->spriteDecorator)
        data->spriteDecorator->update();
}

void Sprite::onOwnerChanged()
{
}
